package controller

import (
	"fmt"
	"gitee.com/worklz/dtm-tcc-demo/common/response"
	"gitee.com/worklz/dtm-tcc-demo/gateway/app/dto"
	"gitee.com/worklz/dtm-tcc-demo/gateway/pkg/global"
	inventorypb "gitee.com/worklz/dtm-tcc-demo/server-inventory/proto/inventory"
	orderpb "gitee.com/worklz/dtm-tcc-demo/server-order/proto/order"
	paymentpb "gitee.com/worklz/dtm-tcc-demo/server-payment/proto/payment"
	"github.com/dtm-labs/client/dtmgrpc"
	"github.com/gin-gonic/gin"
	"github.com/lithammer/shortuuid/v3"
	"google.golang.org/grpc/status"
)

type PurchaseController struct {
	Controller
}

// Handle 购买处理
func (c *PurchaseController) Handle(ctx *gin.Context) {
	// 数据获取校验
	req := dto.PurchaseReq{}
	if err := ctx.ShouldBindJSON(&req); err != nil {
		response.Fail(ctx, err.Error())
		return
	}

	// 创建全局事务ID
	gid := shortuuid.New()
	fmt.Printf("dtm: %s gid: %s grpc_addr: %v\r\n", global.Config.Dtm.GrpcAddr, gid, global.Config.Grpc.ServerAddr)
	var tryCreateOrderRes *orderpb.TccCreateOrderResponse
	err := dtmgrpc.TccGlobalTransaction(global.Config.Dtm.GrpcAddr, gid, func(tcc *dtmgrpc.TccGrpc) (err error) {
		// 调用创建订单服务
		tryCreateOrderRes, err = c.callCreateOrder(gid, &req, tcc)
		if err != nil {
			return
		}
		// 调用库存扣减服务
		if err = c.callDeductInventory(gid, &req, tcc); err != nil {
			return
		}
		// 调用支付服务
		if err = c.callPayment(gid, &req, tryCreateOrderRes, tcc); err != nil {
			return
		}
		return
	})
	if err != nil {
		response.Fail(ctx, fmt.Sprintf("全局事务失败: %v", err))
		return
	}
	response.Success(ctx, dto.PurchaseResData{OrderId: tryCreateOrderRes.OrderId})
}

// 调用创建订单服务
func (c *PurchaseController) callCreateOrder(gid string, req *dto.PurchaseReq, tcc *dtmgrpc.TccGrpc) (tryCreateOrderRes *orderpb.TccCreateOrderResponse, err error) {
	createOrderReq := &orderpb.TccCreateOrderRequest{
		TxId:      gid,
		UserId:    req.UserId,
		ProductId: req.ProductId,
		Quantity:  req.Quantity,
		Amount:    req.Amount,
	}
	tryCreateOrderRes = &orderpb.TccCreateOrderResponse{}
	err = tcc.CallBranch(
		createOrderReq,
		// 服务地址（若用的时etcd等可用服务名称）/proto包名.proto服务名/方法名
		// Try阶段
		global.Config.Grpc.ServerAddr.Order+"/order.OrderService/TryCreateOrder",
		// Confirm阶段
		global.Config.Grpc.ServerAddr.Order+"/order.OrderService/ConfirmCreateOrder",
		// Cancel阶段
		global.Config.Grpc.ServerAddr.Order+"/order.OrderService/CancelCreateOrder",
		// Try阶段的响应
		tryCreateOrderRes,
	)
	// 判断 gRPC 通信层面错误
	if err != nil {
		if st, ok := status.FromError(err); ok {
			err = fmt.Errorf("订单创建异常！%v", st.Message())
			return
		}
		err = fmt.Errorf("订单创建异常：%v", err)
		return
	}
	// 判断业务逻辑错误，告知 DTM 分支执行失败
	if !tryCreateOrderRes.Success {
		err = fmt.Errorf("尝试订单创建异常！%v", err)
		return
	}
	return
}

// 调用库存扣减服务
func (c *PurchaseController) callDeductInventory(gid string, req *dto.PurchaseReq, tcc *dtmgrpc.TccGrpc) (err error) {
	deductInventoryReq := &inventorypb.TccDeductInventoryRequest{
		TxId:      gid,
		ProductId: req.ProductId,
		Quantity:  req.Quantity,
	}
	tryDeductInventoryRes := &inventorypb.TccDeductInventoryResponse{}
	err = tcc.CallBranch(
		deductInventoryReq,
		// 服务地址（若用的时etcd等可用服务名称）/proto包名.proto服务名/方法名
		// Try阶段
		global.Config.Grpc.ServerAddr.Inventory+"/inventory.InventoryService/TryDeductInventory",
		// Confirm阶段
		global.Config.Grpc.ServerAddr.Inventory+"/inventory.InventoryService/ConfirmDeductInventory",
		// Cancel阶段
		global.Config.Grpc.ServerAddr.Inventory+"/inventory.InventoryService/CancelDeductInventory",
		// Try阶段的响应
		tryDeductInventoryRes,
	)
	// 判断 gRPC 通信层面错误
	if err != nil {
		if st, ok := status.FromError(err); ok {
			err = fmt.Errorf("库存扣减异常！%v", st.Message())
			return
		}
		err = fmt.Errorf("库存扣减异常：%v", err)
		return
	}
	// 判断业务逻辑错误，告知 DTM 分支执行失败
	if !tryDeductInventoryRes.Success {
		err = fmt.Errorf("尝试库存扣减异常！%v", err)
		return
	}
	return
}

// 调用支付服务
func (c *PurchaseController) callPayment(gid string, req *dto.PurchaseReq, tryCreateOrderRes *orderpb.TccCreateOrderResponse, tcc *dtmgrpc.TccGrpc) (err error) {
	paymentReq := &paymentpb.TccPaymentRequest{
		TxId:    gid,
		UserId:  req.UserId,
		OrderId: tryCreateOrderRes.OrderId,
		Amount:  req.Amount,
	}
	tryPaymentRes := &paymentpb.TccPaymentResponse{}
	err = tcc.CallBranch(
		paymentReq,
		// 服务地址（若用的时etcd等可用服务名称）/proto包名.proto服务名/方法名
		// Try阶段
		global.Config.Grpc.ServerAddr.Payment+"/payment.PaymentService/TryPayment",
		// Confirm阶段
		global.Config.Grpc.ServerAddr.Payment+"/payment.PaymentService/ConfirmPayment",
		// Cancel阶段
		global.Config.Grpc.ServerAddr.Payment+"/payment.PaymentService/CancelPayment",
		// Try阶段的响应
		tryPaymentRes,
	)
	fmt.Printf("尝试订单支付成功: %v\r\n", tryPaymentRes)
	// 判断 gRPC 通信层面错误
	if err != nil {
		if st, ok := status.FromError(err); ok {
			err = fmt.Errorf("订单支付异常！%v", st.Message())
			return
		}
		err = fmt.Errorf("订单支付异常：%v", err)
		return
	}
	// 判断业务逻辑错误，告知 DTM 分支执行失败
	if !tryPaymentRes.Success {
		err = fmt.Errorf("尝试订单支付异常！%v", tryPaymentRes.Message)
		return
	}
	return
}
